/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include <gtest/gtest.h>

#include <cstring>
#include <limits>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>

#include <mc/common.h>
#include <mc/memory.h>

#include "../../../../../drivers/chip/interface/chip/i_blockio.h"
#include "../../../../../drivers/internal/chip/chip_base/chip.h"
#include "../../../../../drivers/internal/bus/bus_base/bus.h"
#include "../../../../../drivers/internal/hasdef.h"

namespace {

// Mock bus_base 类
class mock_bus : public dev::bus_base {
public:
    mock_bus() : m_lock_count(0) {
    }

    uint32_t read(ACCESS_DATA_S& input) override {
        return HAS_OK;
    }

    uint32_t write(ACCESS_DATA_S& input) override {
        return HAS_OK;
    }

    void bus_lock() override {
        ++m_lock_count;
    }

    void bus_unlock() override {
        if (m_lock_count > 0) {
            --m_lock_count;
        }
    }

    int get_lock_count() const {
        return m_lock_count;
    }

private:
    int m_lock_count;
};

// Mock chip_base 类
class mock_chip : public dev::chip_base {
public:
    mock_chip() : m_plugin_request_result(HAS_OK), m_host_bus(std::make_shared<mock_bus>()) {
        set_host_bus(m_host_bus);
    }

    void set_plugin_request_result(int32_t result) {
        m_plugin_request_result = result;
    }

    void set_plugin_request_error(const std::string& error) {
        m_plugin_request_error = error;
    }

    void set_plugin_request_output(const std::vector<uint8_t>& output) {
        m_plugin_request_output = output;
    }

    int32_t plugin_request(const std::string& plugin_name, const std::string& plugin_cmd,
                           const std::vector<uint8_t>& params, std::vector<uint8_t>& out_buffer,
                           std::string& error) override {
        m_last_plugin_name = plugin_name;
        m_last_plugin_cmd  = plugin_cmd;
        m_last_params      = params;

        if (m_plugin_request_result != HAS_OK) {
            error = m_plugin_request_error;
            return m_plugin_request_result;
        }

        out_buffer = m_plugin_request_output;
        error      = "";
        return HAS_OK;
    }

    std::string get_last_plugin_name() const {
        return m_last_plugin_name;
    }

    std::string get_last_plugin_cmd() const {
        return m_last_plugin_cmd;
    }

    std::vector<uint8_t> get_last_params() const {
        return m_last_params;
    }

private:
    int32_t              m_plugin_request_result;
    std::string          m_plugin_request_error;
    std::vector<uint8_t> m_plugin_request_output;
    std::shared_ptr<mock_bus> m_host_bus;

    std::string          m_last_plugin_name;
    std::string          m_last_plugin_cmd;
    std::vector<uint8_t> m_last_params;
};

// Test fixture
class i_blockio_test : public ::testing::Test {
protected:
    void SetUp() override {
        m_mock_chip = std::make_shared<mock_chip>();
        m_blockio   = std::make_unique<dev::Interface_BlockIO>();
        m_blockio->m_chip = m_mock_chip.get();
    }

    void TearDown() override {
        m_blockio.reset();
        m_mock_chip.reset();
    }

    std::shared_ptr<mock_chip>           m_mock_chip;
    std::unique_ptr<dev::Interface_BlockIO> m_blockio;
};

// ========== seri_pack 和 seri_unpack 测试 ==========

TEST_F(i_blockio_test, seri_pack_unpack_nil) {
    std::vector<dev::seri_value> data = {nullptr};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    EXPECT_TRUE(std::holds_alternative<std::nullptr_t>(unpacked[0]));
}

TEST_F(i_blockio_test, seri_pack_unpack_boolean_true) {
    std::vector<dev::seri_value> data = {true};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    EXPECT_TRUE(std::holds_alternative<bool>(unpacked[0]));
    EXPECT_TRUE(std::get<bool>(unpacked[0]));
}

TEST_F(i_blockio_test, seri_pack_unpack_boolean_false) {
    std::vector<dev::seri_value> data = {false};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    EXPECT_TRUE(std::holds_alternative<bool>(unpacked[0]));
    EXPECT_FALSE(std::get<bool>(unpacked[0]));
}

TEST_F(i_blockio_test, seri_pack_unpack_integer_zero) {
    std::vector<dev::seri_value> data = {static_cast<int64_t>(0)};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    // 零值会被反序列化为 int8_t 或 uint8_t
    EXPECT_TRUE(std::holds_alternative<int8_t>(unpacked[0]) || 
                std::holds_alternative<uint8_t>(unpacked[0]));
    int64_t val = 0;
    std::visit([&val](const auto& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_integral_v<T>) {
            val = static_cast<int64_t>(v);
        }
    }, unpacked[0]);
    EXPECT_EQ(val, 0);
}

TEST_F(i_blockio_test, seri_pack_unpack_integer_byte) {
    std::vector<dev::seri_value> data = {static_cast<int8_t>(42)};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    int64_t val = 0;
    std::visit([&val](const auto& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_integral_v<T>) {
            val = static_cast<int64_t>(v);
        }
    }, unpacked[0]);
    EXPECT_EQ(val, 42);
}

TEST_F(i_blockio_test, seri_pack_unpack_integer_word) {
    std::vector<dev::seri_value> data = {static_cast<int16_t>(0x1234)};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    int64_t val = 0;
    std::visit([&val](const auto& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_integral_v<T>) {
            val = static_cast<int64_t>(v);
        }
    }, unpacked[0]);
    EXPECT_EQ(val, 0x1234);
}

TEST_F(i_blockio_test, seri_pack_unpack_integer_dword) {
    std::vector<dev::seri_value> data = {static_cast<int32_t>(0x12345678)};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    int64_t val = 0;
    std::visit([&val](const auto& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_integral_v<T>) {
            val = static_cast<int64_t>(v);
        }
    }, unpacked[0]);
    EXPECT_EQ(val, 0x12345678);
}

TEST_F(i_blockio_test, seri_pack_unpack_integer_qword) {
    std::vector<dev::seri_value> data = {static_cast<int64_t>(0x123456789ABCDEF0LL)};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    int64_t val = 0;
    std::visit([&val](const auto& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_integral_v<T>) {
            val = static_cast<int64_t>(v);
        }
    }, unpacked[0]);
    EXPECT_EQ(val, 0x123456789ABCDEF0LL);
}

TEST_F(i_blockio_test, seri_pack_unpack_integer_negative) {
    std::vector<dev::seri_value> data = {static_cast<int32_t>(-12345)};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    int64_t val = 0;
    std::visit([&val](const auto& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_integral_v<T>) {
            val = static_cast<int64_t>(v);
        }
    }, unpacked[0]);
    EXPECT_EQ(val, -12345);
}

TEST_F(i_blockio_test, seri_pack_unpack_real_float) {
    std::vector<dev::seri_value> data = {static_cast<float>(3.14159f)};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    EXPECT_TRUE(std::holds_alternative<double>(unpacked[0]));
    EXPECT_NEAR(std::get<double>(unpacked[0]), 3.14159, 0.0001);
}

TEST_F(i_blockio_test, seri_pack_unpack_real_double) {
    std::vector<dev::seri_value> data = {3.141592653589793};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    EXPECT_TRUE(std::holds_alternative<double>(unpacked[0]));
    EXPECT_DOUBLE_EQ(std::get<double>(unpacked[0]), 3.141592653589793);
}

TEST_F(i_blockio_test, seri_pack_unpack_short_string) {
    std::string                  str = "Hello";
    std::vector<dev::seri_value> data = {str};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    EXPECT_TRUE(std::holds_alternative<std::string>(unpacked[0]));
    EXPECT_EQ(std::get<std::string>(unpacked[0]), "Hello");
}

TEST_F(i_blockio_test, seri_pack_unpack_long_string_16bit) {
    std::string str(100, 'A');
    std::vector<dev::seri_value> data = {str};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    EXPECT_TRUE(std::holds_alternative<std::string>(unpacked[0]));
    EXPECT_EQ(std::get<std::string>(unpacked[0]), str);
}

TEST_F(i_blockio_test, seri_pack_unpack_long_string_32bit) {
    std::string str(70000, 'B');
    std::vector<dev::seri_value> data = {str};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    EXPECT_TRUE(std::holds_alternative<std::string>(unpacked[0]));
    EXPECT_EQ(std::get<std::string>(unpacked[0]), str);
}

TEST_F(i_blockio_test, seri_pack_unpack_binary_data) {
    std::vector<uint8_t>        binary = {0x01, 0x02, 0x03, 0x04, 0x05};
    std::vector<dev::seri_value> data = {binary};
    auto                         packed = m_blockio->seri_pack(data);
    auto                         unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 1);
    // 二进制数据会被转换为字符串
    EXPECT_TRUE(std::holds_alternative<std::string>(unpacked[0]));
    std::string result = std::get<std::string>(unpacked[0]);
    EXPECT_EQ(result.size(), binary.size());
    EXPECT_EQ(std::memcmp(result.data(), binary.data(), binary.size()), 0);
}

TEST_F(i_blockio_test, seri_pack_unpack_mixed_types) {
    std::vector<dev::seri_value> data = {
        nullptr, true, false, static_cast<int32_t>(42), static_cast<double>(3.14),
        std::string("test"), std::vector<uint8_t>{0xAA, 0xBB}
    };
    auto packed   = m_blockio->seri_pack(data);
    auto unpacked = m_blockio->seri_unpack(packed);

    EXPECT_EQ(unpacked.size(), 7);
    EXPECT_TRUE(std::holds_alternative<std::nullptr_t>(unpacked[0]));
    EXPECT_TRUE(std::holds_alternative<bool>(unpacked[1]));
    EXPECT_TRUE(std::get<bool>(unpacked[1]));
    EXPECT_TRUE(std::holds_alternative<bool>(unpacked[2]));
    EXPECT_FALSE(std::get<bool>(unpacked[2]));
    // 检查整数
    int64_t val = 0;
    std::visit([&val](const auto& v) {
        using T = std::decay_t<decltype(v)>;
        if constexpr (std::is_integral_v<T>) {
            val = static_cast<int64_t>(v);
        }
    }, unpacked[3]);
    EXPECT_EQ(val, 42);
    // 检查浮点数
    EXPECT_TRUE(std::holds_alternative<double>(unpacked[4]));
    EXPECT_NEAR(std::get<double>(unpacked[4]), 3.14, 0.01);
    // 检查字符串
    EXPECT_TRUE(std::holds_alternative<std::string>(unpacked[5]));
    EXPECT_EQ(std::get<std::string>(unpacked[5]), "test");
    // 检查二进制数据（转换为字符串）
    EXPECT_TRUE(std::holds_alternative<std::string>(unpacked[6]));
}

TEST_F(i_blockio_test, seri_unpack_empty_data) {
    std::vector<uint8_t> empty;
    auto                 unpacked = m_blockio->seri_unpack(empty);
    EXPECT_TRUE(unpacked.empty());
}

TEST_F(i_blockio_test, seri_unpack_invalid_data_throws_exception) {
    std::vector<uint8_t> invalid = {0xFF, 0xFF, 0xFF};
    EXPECT_THROW(m_blockio->seri_unpack(invalid), mc::method_call_exception);
}

// ========== PluginRequest 测试 ==========

TEST_F(i_blockio_test, plugin_request_success) {
    // 设置 mock chip 返回成功
    std::vector<uint8_t> output(129, 0xAA);
    m_mock_chip->set_plugin_request_output(output);

    std::map<std::string, std::string> context;
    std::string                        plugin_name = "test_plugin";
    std::string                        plugin_cmd  = "test_cmd";
    std::vector<uint8_t>               params_binary = {0x01, 0x02, 0x03};

    // 序列化参数
    std::vector<dev::seri_value> params_data = {params_binary};
    std::vector<uint8_t>         serialized_params = m_blockio->seri_pack(params_data);

    // 调用 PluginRequest
    std::vector<uint8_t> result = m_blockio->PluginRequest(context, plugin_name, plugin_cmd,
                                                            serialized_params);

    // 验证 mock chip 被正确调用
    EXPECT_EQ(m_mock_chip->get_last_plugin_name(), plugin_name);
    EXPECT_EQ(m_mock_chip->get_last_plugin_cmd(), plugin_cmd);

    // 反序列化结果
    std::vector<dev::seri_value> deserialized_result = m_blockio->seri_unpack(result);

    // 验证结果格式：{true, binary_data}
    EXPECT_GE(deserialized_result.size(), 2);
    EXPECT_TRUE(std::holds_alternative<bool>(deserialized_result[0]));
    EXPECT_TRUE(std::get<bool>(deserialized_result[0]));
    EXPECT_TRUE(std::holds_alternative<std::string>(deserialized_result[1]));
    std::string result_data = std::get<std::string>(deserialized_result[1]);
    EXPECT_EQ(result_data.size(), 129);
}

TEST_F(i_blockio_test, plugin_request_success_with_leading_zeros) {
    // 设置 mock chip 返回带前导0的数据
    std::vector<uint8_t> output = {0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC};
    m_mock_chip->set_plugin_request_output(output);

    std::map<std::string, std::string> context;
    std::string                        plugin_name = "test_plugin";
    std::string                        plugin_cmd  = "test_cmd";
    std::vector<uint8_t>               params_binary = {0x01};

    std::vector<dev::seri_value> params_data = {params_binary};
    std::vector<uint8_t>         serialized_params = m_blockio->seri_pack(params_data);

    std::vector<uint8_t> result = m_blockio->PluginRequest(context, plugin_name, plugin_cmd,
                                                            serialized_params);

    std::vector<dev::seri_value> deserialized_result = m_blockio->seri_unpack(result);
    EXPECT_GE(deserialized_result.size(), 2);
    std::string result_data = std::get<std::string>(deserialized_result[1]);
    // 前导0应该被去除，然后在末尾补足到129字节
    EXPECT_EQ(result_data.size(), 129);
    // 前三个字节是实际数据（前导0已被去除）
    EXPECT_EQ(static_cast<uint8_t>(result_data[0]), 0xAA);
    EXPECT_EQ(static_cast<uint8_t>(result_data[1]), 0xBB);
    EXPECT_EQ(static_cast<uint8_t>(result_data[2]), 0xCC);
    // 后面补0
    EXPECT_EQ(static_cast<uint8_t>(result_data[3]), 0x00);
}

TEST_F(i_blockio_test, plugin_request_success_output_less_than_129_bytes) {
    // 设置 mock chip 返回少于129字节的数据
    std::vector<uint8_t> output = {0xAA, 0xBB};
    m_mock_chip->set_plugin_request_output(output);

    std::map<std::string, std::string> context;
    std::string                        plugin_name = "test_plugin";
    std::string                        plugin_cmd  = "test_cmd";
    std::vector<uint8_t>               params_binary = {0x01};

    std::vector<dev::seri_value> params_data = {params_binary};
    std::vector<uint8_t>         serialized_params = m_blockio->seri_pack(params_data);

    std::vector<uint8_t> result = m_blockio->PluginRequest(context, plugin_name, plugin_cmd,
                                                            serialized_params);

    std::vector<dev::seri_value> deserialized_result = m_blockio->seri_unpack(result);
    EXPECT_GE(deserialized_result.size(), 2);
    std::string result_data = std::get<std::string>(deserialized_result[1]);
    // 应该补足到129字节
    EXPECT_EQ(result_data.size(), 129);
    EXPECT_EQ(static_cast<uint8_t>(result_data[0]), 0xAA);
    EXPECT_EQ(static_cast<uint8_t>(result_data[1]), 0xBB);
    // 后面的字节应该是0
    for (size_t i = 2; i < 129; ++i) {
        EXPECT_EQ(static_cast<uint8_t>(result_data[i]), 0x00);
    }
}

TEST_F(i_blockio_test, plugin_request_failure) {
    // 设置 mock chip 返回失败
    m_mock_chip->set_plugin_request_result(HAS_ERR);
    m_mock_chip->set_plugin_request_error("Test error");

    std::map<std::string, std::string> context;
    std::string                        plugin_name = "test_plugin";
    std::string                        plugin_cmd  = "test_cmd";
    std::vector<uint8_t>               params_binary = {0x01};

    std::vector<dev::seri_value> params_data = {params_binary};
    std::vector<uint8_t>         serialized_params = m_blockio->seri_pack(params_data);

    // 应该抛出异常
    EXPECT_THROW(m_blockio->PluginRequest(context, plugin_name, plugin_cmd, serialized_params),
                 mc::method_call_exception);
}

TEST_F(i_blockio_test, plugin_request_with_string_params) {
    // 测试字符串参数
    std::vector<uint8_t> output(129, 0xBB);
    m_mock_chip->set_plugin_request_output(output);

    std::map<std::string, std::string> context;
    std::string                        plugin_name = "test_plugin";
    std::string                        plugin_cmd  = "test_cmd";
    std::string                        params_string = "test_string";

    std::vector<dev::seri_value> params_data = {params_string};
    std::vector<uint8_t>         serialized_params = m_blockio->seri_pack(params_data);

    std::vector<uint8_t> result = m_blockio->PluginRequest(context, plugin_name, plugin_cmd,
                                                            serialized_params);

    std::vector<dev::seri_value> deserialized_result = m_blockio->seri_unpack(result);
    EXPECT_GE(deserialized_result.size(), 2);
    EXPECT_TRUE(std::get<bool>(deserialized_result[0]));
}

TEST_F(i_blockio_test, plugin_request_with_mixed_params) {
    // 测试混合类型参数
    std::vector<uint8_t> output(129, 0xCC);
    m_mock_chip->set_plugin_request_output(output);

    std::map<std::string, std::string> context;
    std::string                        plugin_name = "test_plugin";
    std::string                        plugin_cmd  = "test_cmd";

    std::vector<dev::seri_value> params_data = {
        static_cast<int32_t>(42), std::string("test"), std::vector<uint8_t>{0x01, 0x02}
    };
    std::vector<uint8_t> serialized_params = m_blockio->seri_pack(params_data);

    std::vector<uint8_t> result = m_blockio->PluginRequest(context, plugin_name, plugin_cmd,
                                                            serialized_params);

    std::vector<dev::seri_value> deserialized_result = m_blockio->seri_unpack(result);
    EXPECT_GE(deserialized_result.size(), 2);
    EXPECT_TRUE(std::get<bool>(deserialized_result[0]));
}

} // namespace

